﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using RayDen.Library.Core.Primitives;

namespace RayDen.Library.Data
{
    public class GenericProceduralTexture : BaseProceduralTexture
    {
        private RgbSpectrum baseScale;

        private readonly Expression<Func<float, float, RgbSpectrum>> textureExpression;
        private  Func<float, float, RgbSpectrum> textureFunc;

        public GenericProceduralTexture(int w, int h, string expression)
            : base(w, h)
        {
            this.textureExpression = CreateExpression(expression);
            this.baseScale = new RgbSpectrum(1f);
        }

        public override RgbSpectrumInfo Sample(float u, float v)
        {
            if (textureFunc == null)
                this.textureFunc = textureExpression.Compile();
            return (RgbSpectrumInfo) this.textureFunc(u, v);
        }



        /*
         
         * u,v - tex coords float
         * e - epsilon (random float)
         * p - barycentric sample point (u,v,1 - u - v) - vector
         * n - Noise(p) call
         * n3d - Noise3D(p) call
         * 
         */
        public static Expression<Func<float, float, RgbSpectrum>> CreateExpression(string expressionString)
        {
            if (string.IsNullOrWhiteSpace(expressionString))
            {
                throw new ArgumentNullException("expressionString");
            }

            var expressionData = expressionString.ToLowerInvariant().Trim();

            var u = Expression.Parameter(typeof (float), "u");
            var v = Expression.Parameter(typeof(float), "v");
            var e = Expression.Parameter(typeof(float), "e");
            var normalizer = Expression.Parameter(typeof(RgbSpectrum), "baseScale");

            var evaluator = new DoubleExpressionEvaluator();
            throw new NotImplementedException();
            
        }


    }


    internal sealed class Operation
    {
        private readonly int precedence;
        private readonly string name;
        private readonly Func<Expression, Expression, Expression> operation;

        public static readonly Operation Addition = new Operation(1, Expression.Add, "Addition");
        public static readonly Operation Subtraction = new Operation(1, Expression.Subtract, "Subtraction");
        public static readonly Operation Multiplication = new Operation(2, Expression.Multiply, "Multiplication");
        public static readonly Operation Division = new Operation(2, Expression.Divide, "Division");


        private static readonly Dictionary<char, Operation> Operations = new Dictionary<char, Operation>
        {
            { '+', Addition },
            { '-', Subtraction },
            { '*', Multiplication},
            { '/', Division },
        };

        private Operation(int precedence, Func<Expression, Expression, Expression> operation, string name)
        {
            this.precedence = precedence;
            this.operation = operation;
            this.name = name;
        }

        public int Precedence
        {
            get { return precedence; }
        }

        public static explicit operator Operation(char operation)
        {
            Operation result;

            if (Operations.TryGetValue(operation, out result))
            {
                return result;
            }
            else
            {
                throw new InvalidCastException();
            }
        }

        public Expression Apply(Expression left, Expression right)
        {
            return operation(left, right);
        }

        public static bool IsDefined(char operation)
        {
            return Operations.ContainsKey(operation);
        }
    }
}
